#include"execution_engine.h"
#include<iostream>

bool Engine::Stepping() {
    std::cout << "Engine Stepping at global clock: " << global_clock << std::endl;
    this->WCM->do_the_real_thing(); //在每个周期开始时处理写延迟控制模块的写入请求
    if (this->resume_clock == -1) {
        //当前处于暂停指令派发的状态，Stop
        // global_clock++; //模拟器的逻辑时钟前进一个周期
        return false;
    } else if (global_clock <= this->resume_clock) {
        //当前处于暂停指令派发的状态，StopNCycles
        // global_clock++;
        return false;
    }
    //其余情况正常派发指令
    // global_clock++; //模拟器的逻辑时钟前进一个周期
    //将该周期发射的所有指令存入instr buffer
    addr_size_t new_PC = Fetching(PC);
    std::cout << "Fetched new PC: " << std::hex << new_PC << std::dec << std::endl;
    if (new_PC == 0) {
        return false; //如果取指失败，返回false
    }
    std::cout << "Instructions fetched into buffer at global clock: " << global_clock << std::endl;
    std::cout << "Instruction buffer size: " << this->instr_buffer->size() << std::endl;
    std::cout << this->instr_buffer << std::endl;
    //遍历instr buffer将每一条指令分发到硬件模块上
    for(int i = 0; i < this->instr_buffer->size(); i++) {
        if (!InstrDistribution(this->instr_buffer->at(i))) {
            return false;
        }
    }
    std::cout << "Instructions distributed successfully at global clock: " << global_clock << std::endl;

    ClearInstrBuffer(); //在该周期执行结束时，将instr buffer清空，准备存储下一周期的指令
    if(this->PC == this->End_PC) {
        return false; //如果当前PC已经到达End_PC，表明程序执行完毕，返回false
    }
    //先判断当前PC指针是否为End_PC，如果是则表明程序执行完毕，返回false
    //否则更新PC指针，指向下一条要执行的指令地址
    this->PC = new_PC; //更新PC指针，指向下一条要执行的指令地址
    std::cout << "Engine Stepping completed at global clock: " << global_clock << std::endl;
    return true;
}

bool Engine::StopNCycles(int cycles) {
    this->resume_clock = global_clock + cycles;
    return true;
}

bool Engine::Stop() {
    this->resume_clock = -1;
    return true;
}

bool Engine::Resume() {
    this->resume_clock = 0;
    return true;
}

//TODO:为了处理sbr指令中跳转到执行包中间指令的情况，需要设计一个方法通过执行包内部任意一条指令来获取执行包第一条指令
addr_size_t Engine::Fetching(addr_size_t addr) {
    instr_unit* first_instr = this->instr_memory->get_instr(addr);
    if (addr == first_instr->head_addr) {
        //如果传入的地址是某个执行包的第一条指令地址，则无需再次取指
    } else {
        //如果传入的地址不是某个执行包的第一条指令地址，则需要通过该地址获取该执行包的第一条指令地址，再次取指
        first_instr = this->instr_memory->get_instr(first_instr->head_addr);
    }

    if (first_instr == NULL) {
        return 0;
    }
    this->instr_buffer->push_back(first_instr->instr);
    std::cout << "Fetched instruction at address: " << std::hex << addr << std::dec << std::endl;
    std::cout << first_instr->para_instr_nums << std::endl;
    std::cout << "Fetching instr_buffer size : " << this->instr_buffer->size() << std::endl;
    //如果该指令后还有并行发射的指令，则继续取指
    if (first_instr->para_instr_nums != 0) {
        //用于循环迭代的中间保存变量
        addr_size_t next_addr = addr;
        instruction* temp_instr = first_instr->instr;
        instruction* para_instr;

        for (int i = 0; i < first_instr->para_instr_nums; i++) {
            
            next_addr = temp_instr->get_next_instr_addr(next_addr);
            std::cout << "Fetching parallel instruction at address: " << std::hex << next_addr << std::dec << std::endl;
            // para_instr = this->instr_memory->get_instr(next_addr)->instr;
            if (this->instr_memory->get_instr(next_addr) == NULL) {
                std::cout << "ERROR: FAILED TO FETCH PARALLEL INSTRUCTION!" << std::endl;
            }
            para_instr = this->instr_memory->get_instr(next_addr)->instr;
            std::cout << 1;
            this->instr_buffer->push_back(para_instr);
            temp_instr = para_instr;
        }
        return temp_instr->get_next_instr_addr(next_addr);
    }
    //更新PC指针，指向下一条要执行的指令地址
    return first_instr->instr->get_next_instr_addr(addr);
}

void Engine::ClearInstrBuffer() {
    this->instr_buffer->clear();
}

//通过对传入的instr指令的操作码进行解析，引擎应能够判断出将其发送给哪个硬件部件去执行
//例如vfmulad则分配给VPE，load/store则分配给memory module，如果是call 通信函数则分配给comm module
bool Engine::InstrDistribution(instruction* instr) {
    int8_t hw_no = instr->get_hw_id();
    std::cout << "Distributing instruction to hardware ID: " << (int)hw_no << std::endl;
    switch (hw_no) {
        case VMAC_M1:
            this->vpu->Execute_VMAC1(instr);
            // std::cout << "Distributing to VPU VMAC_M1" << std::endl;
            break;
        case VMAC_M2:
            this->vpu->Execute_VMAC2(instr);
            break;
        case VMAC_M3:
            this->vpu->Execute_VMAC3(instr);
            std::cout << "Distributing to VPU VMAC_M3" << std::endl;
            break;
        case VIEU_:
            this->vpu->Execute_VIEU(instr);
            break;
        case VLDST_0_:

        case VLDST_1_:

        case SMAC_M1:
            std::cout << "Distributing to SPU SMAC_M1" << std::endl;
            this->spu->Execute_SMAC1(instr);
            break;
        case SMAC_M2:
            this->spu->Execute_SMAC2(instr);
            break;
        case SIEU_:
            this->spu->Execute_SIEU(instr);
            break;
        case SLDST_:
            this->spu->Execute_SLDST(instr);
            break;
        case SBR_:
            this->spu->Execute_SBR(instr);
            break;
        default:
            return false;
    }
    return true;
}

void GroupEngine::add_node_engine(Engine* engine) {
    // for (int i = 0; i < this->node_engines->size(); i++) {
    //     if (this->node_engines->at(i) == nullptr) {
    //         this->node_engines->at(i) = engine;
    //         break;
    //     }
    // }
    this->node_engines->push_back(engine);
}

bool GroupEngine::Execute() {
    bool flag = false;
    while (true) {
        flag = false;
        std::cout << "GroupEngine executing at global clock: " << global_clock << std::endl;
        std::cout << "Number of DSP cores in GroupEngine: " << this->node_engines->size() << std::endl;
        for (int i = 0; i < this->node_engines->size(); i++) {
            std::cout << "Checking DSP core " << i << std::endl;
            if (this->node_engines->at(i) != nullptr) {
                flag = true;
                if (!this->node_engines->at(i)->Stepping()) {
                    std::cout << "DSP core " << i << " has completed execution." << std::endl;
                    this->node_engines->at(i) = nullptr; //该DSP核执行完毕，置空
                }
            }
        }
        //!!!ATTETION:目前版本仅用于指令单元测试，仅可以保证仅有一个线程组执行，无多线程组并行的情况
        global_clock++; //整个线程组的时钟周期前进一个周期
        //如果该线程组包含的所有DSP核均执行完毕，跳出循环
        if (!flag) {
            break;
        }
    }
}

void ClusterEngine::add_group_engine(int thread_id ,GroupEngine* engine) {
    this->group_engines->insert(std::make_pair(thread_id, engine));
}

GroupEngine* ClusterEngine::get_group_engine(int group_id) {
    auto it = this->group_engines->find(group_id);
    if (it == this->group_engines->end()) {
        return nullptr;
    }
    return it->second;
}